using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Krilib.Bridges;
using Krilib.Plugin;

namespace Krile.Forms.MainForm
{
    public partial class Main : Form, IMainForm
    {

        internal Data.TabData[] TabDatas
        {
            get
            {
                List<Data.TabData> retdats = new List<Krile.Data.TabData>();
                foreach (var dat in xTab.GetItemBaseArray())
                {
                    var tlItem = dat as MainForm.Controls.XTab.XTabTLItem;
                    if (tlItem != null)
                    {
                        retdats.Add(tlItem.TabData);
                    }
                }
                return retdats.ToArray();
            }
        }

        public Krilib.Data.Status SelectedStatus
        {
            get
            {
                if (timeline.SelectedStatus == null)
                    return null;
                else
                    return timeline.SelectedStatus.BaseStatus;
            }
        }

        internal Data.XStatus SelectedXStatus
        {
            get { return timeline.SelectedStatus; }
        }

        internal Controls.XTab.XTab TabControl { get { return xTab; } }

        public Main()
        {
            InitializeComponent();
            Inputter.OnChangeVLocRequired += new EventHandler(Inputter_OnChangeVLocRequired);
            Inputter.OnShowRequired += new EventHandler(Inputter_OnShowRequired);
            Inputter.OnCloseRequired += new EventHandler(Inputter_OnCloseRequired);
            mainMenu.Leave += new EventHandler(mainMenu_Leave);
            
        }

        protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Left:
                case Keys.Right:
                case Keys.Tab:
                    e.IsInputKey = true;
                    break;
            }
            base.OnPreviewKeyDown(e);
        }

        private void Main_Load(object sender, EventArgs e)
        {
            tlCsChanged = new EventHandler(TabData_ContainStatusChanged);

            //title init
            this.Text = this.Text + " " + Define.VersionString;

            xTab.OnSelectedTabChanged += new EventHandler(xTab_OnSelectedTabChanged);

            Inputter.Init();
            timeline.InitScrollbar();

            Core.ImageManager.DownloadStateChanged += new EventHandler(ImageManager_DownloadStateChanged);

            ReflectState();
        }

        void ImageManager_DownloadStateChanged(object sender, EventArgs e)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new EventHandler(ImageManager_DownloadStateChanged));
            }
            else
            {
                SImageDownloadState.Visible = Core.ImageManager.Downloading;
            }
        }

        private void ReflectState()
        {
            //restore configuration
            if (Core.Config.State.PostBoxInTop)
            {
                this.mainSplitter.Panel1.Controls.Add(this.postPanel);
                this.mainSplitter.Panel2.Controls.Add(this.listPanel);
                this.mainSplitter.FixedPanel = FixedPanel.Panel1;
            }

            SetPostboxState(Core.Config.State.ShowPostBox);
            if (Core.Config.State.RecvGTL)
            {
                Core.Config.State.RecvGTL = false;
                ToggleRecvGTL(null, null);
            }

            if (!Core.Config.State.ShowMenubar)
            {
                Core.Config.State.ShowMenubar = true;
                ToggleMenubar(null, null);
            }

            if (!Core.Config.State.ShowToolbar)
            {
                Core.Config.State.ShowToolbar = true;
                ToggleToolbar(null, null);
            }

            if (!Core.Config.State.ShowStatusbar)
            {
                Core.Config.State.ShowStatusbar = true;
                ToggleStatusbar(null, null);
            }

            if (Core.Config.State.SilentMode)
                SetSilentMode(true);

            mainSplitter.SplitterDistance = Core.Config.State.MainSplitOffset;

            if (Core.Config.State.TabDatas != null)
            {
                foreach (var td in Core.Config.State.TabDatas)
                {
                    var ntab = new MainForm.Controls.XTab.XTabTLItem(xTab, td);
                    xTab.AddTab(ntab);
                }
            }

            //initial tab
            if (Core.Config.IsFirstboot)
            {
                xTab.AddTab(MainForm.Controls.XTab.XTabTLItem.CreateATLTab(xTab));
            }

            ApplyConfig();
            this.Refresh();
        }

        public IInputControlManager InputControlManager
        {
            get { return Inputter; }
        }

        internal void ReRegistTimeline()
        {
            xTab_OnSelectedTabChanged(null, null);
        }

        EventHandler tlCsChanged;
        MainForm.Controls.XTab.XTabTLItem prevSelected = null;
        void xTab_OnSelectedTabChanged(object sender, EventArgs e)
        {
            if (prevSelected != null)
            {
                prevSelected.TabData.ContainStatusChanged -= tlCsChanged;
                prevSelected.ScrollOffset = timeline.ScrollValue;
                Inputter.ClearReplyTo();
                var svd = from Krilib.Data.Service svc in Inputter.SelectedServices
                          select svc.Id;
                prevSelected.TabData.LinkedServiceIds = svd.ToArray<string>();
            }

            prevSelected = (MainForm.Controls.XTab.XTabTLItem)xTab.SelectedItem;

            if (prevSelected != null)
            {
                timeline.Deselect(false);
                prevSelected.TabData.ContainStatusChanged += tlCsChanged;
                timeline.Statuses = prevSelected.TabData.ContainStatuses;
                timeline.ScrollValue = prevSelected.ScrollOffset;
                timeline.InitScrollbar();
                if (prevSelected.TabData.LinkedServiceIds != null)
                {
                    var svcs = from string id in prevSelected.TabData.LinkedServiceIds
                               where Core.PluginManager.LookupService(id) != null
                               select Core.PluginManager.LookupService(id);
                    Inputter.SelectedServices = svcs.ToArray<Krilib.Data.Service>();
                }
            }
            else
            {
                timeline.Statuses = null;
                timeline.ScrollValue = 0;
                timeline.InitScrollbar();
                Inputter.SelectedServices = null;
            }
        }

        void TabData_ContainStatusChanged(object sender, EventArgs e)
        {
            timeline.Statuses = prevSelected.TabData.ContainStatuses;
        }

        void Inputter_OnChangeVLocRequired(object sender, EventArgs e)
        {
            this.SuspendLayout();
            if (Core.Config.State.PostBoxInTop = !Core.Config.State.PostBoxInTop)
            {
                this.mainSplitter.SplitterDistance = this.mainSplitter.Height - this.mainSplitter.SplitterDistance;
                this.mainSplitter.Panel1.Controls.Add(this.postPanel);
                this.mainSplitter.Panel2.Controls.Add(this.listPanel);
                this.mainSplitter.FixedPanel = FixedPanel.Panel1;
            }
            else
            {
                this.mainSplitter.SplitterDistance = this.mainSplitter.Height - this.mainSplitter.SplitterDistance;
                this.mainSplitter.Panel2.Controls.Add(this.postPanel);
                this.mainSplitter.Panel1.Controls.Add(this.listPanel);
                this.mainSplitter.FixedPanel = FixedPanel.Panel2;
            }
            this.ResumeLayout();
        }

        void Inputter_OnShowRequired(object sender, EventArgs e)
        {
            SetPostboxState(true);
        }

        void Inputter_OnCloseRequired(object sender, EventArgs e)
        {
            SetPostboxState(false);
        }

        private void mainSplitter_SplitterMoved(object sender, SplitterEventArgs e)
        {
            Core.Config.State.MainSplitOffset = mainSplitter.SplitterDistance;
        }

        #region MenuCommand

        private void ShowPluginManager(object sender, EventArgs e)
        {
            using (var pm = new PManForm.PluginManager())
            {
                pm.ShowDialog();
            }
        }

        internal void ShowConfig()
        {
            using (var cog = new Forms.ConfigForm.Config())
            {
                cog.StartPosition = FormStartPosition.CenterParent;
                cog.ShowDialog();
            }
        }

        private void ShowConfig(object sender, EventArgs e)
        {
            ShowConfig();
        }

        internal void ApplyConfig()
        {
            timeline.ScrollbarWidth = Core.Config.Appearance.ScrollbarWidth;
            timeline.SurfaceRedraw(true);
        }

        private void Debug_DoGC(object sender, EventArgs e)
        {
            SetStatusText("Collecting garbage...");
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            SetStatusText(String.Empty);
            SetBackStatusText("GC Completed.");
        }

        private void Debug_ShowDebugReport(object sender, EventArgs e)
        {
            using (var rep = new Dialogs.Show.Reporter())
            {
                rep.ShowDialog();
            }
        }

        private void MSilentMode_Click(object sender, EventArgs e)
        {
            SetSilentMode(!Core.Config.State.SilentMode);
        }

        internal void SetSilentMode(bool silent)
        {
            Core.Config.State.SilentMode = silent;
            MSilentMode.Checked = silent;
        }

        private void MSendTray_Click(object sender, EventArgs e)
        {
            this.Visible = false;
        }

        private void MExit_Click(object sender, EventArgs e)
        {
            Core.ExitApp();
        }

        private void MAddTab_Click(object sender, EventArgs e)
        {
            CreateTab();
        }

        public void CreateTab()
        {
            using (var te = new Dialogs.Editor.TabEditor())
            {
                if (te.ShowDialog() == DialogResult.OK)
                {
                    xTab.AddTab(
                        new MainForm.Controls.XTab.XTabTLItem(
                            xTab,
                            te.TabData));
                }
            }
        }

        public void AddTab()
        {
            this.CreateTab();
        }

        public void AddTab(string name, Krilib.Data.SearchDataCluster cluster, string[] svids)
        {
            this.CreateTab(name, cluster, false, svids);
        }


        public void CreateTab(string name, Krilib.Data.SearchDataCluster cluster, bool unNotify, string[] svids)
        {
            var ntd = new Data.TabData();
            ntd.ContainSearchData = cluster;
            ntd.Name = name;
            ntd.UnNotify = unNotify;
            ntd.LinkedServiceIds = svids;
            xTab.AddTab(
                new MainForm.Controls.XTab.XTabTLItem(
                    xTab,
                    ntd));
        }

        private void CreateQuickTab(object sender, EventArgs e)
        {
            CreateQuickTab();
        }

        public void CreateQuickTab()
        {
            var item = xTab.SelectedItem as MainForm.Controls.XTab.XTabTLItem;
            if (item != null)
            {
                using (var qt = new Krile.Forms.Dialogs.Editor.QuickTab())
                {
                    qt.TabData = item.TabData;
                    if (qt.ShowDialog() == DialogResult.OK)
                    {
                        xTab.AddTab(
                            new MainForm.Controls.XTab.XTabTLItem(
                                xTab,
                                qt.TabData));
                    }
                }
            }
        }

        private void MAddATL_Click(object sender, EventArgs e)
        {
            xTab.AddTab(MainForm.Controls.XTab.XTabTLItem.CreateATLTab(xTab));
        }

        internal void ToggleRecvGTL(object sender, EventArgs e)
        {
            Core.Config.State.RecvGTL = !Core.Config.State.RecvGTL;
            MRecvGTL.Checked = Core.Config.State.RecvGTL;
            TRecvGTL.Checked = Core.Config.State.RecvGTL;
            foreach (var svc in Core.PluginManager.GetServiceEnumerator())
            {
                svc.ParentAgent.Status.RecvGlobalTimeline = Core.Config.State.RecvGTL;
            }
            if (Core.Config.Behavior.ReloadOnGTLImplyStateChanged)
            {
                foreach (var svc in Core.PluginManager.GetServiceEnumerator())
                {
                    svc.ParentAgent.Status.ReceiveStatuses();
                }
            }
        }

        private void RefreshTimeline(object sender, EventArgs e)
        {
            foreach (var svc in Core.PluginManager.GetServiceEnumerator())
            {
                svc.ParentAgent.Status.ReceiveStatuses();
            }
        }

        internal void MAllReaded_Click(object sender, EventArgs e)
        {
            timeline.DoEnumOperationWithProtectedThread((a) => a.Unread = false);
            timeline.Refresh();
        }

        private void TogglePostBox(object sender, EventArgs e)
        {
            SetPostboxState(!Core.Config.State.ShowPostBox);
        }

        internal void SetPostboxState(bool visible)
        {
            if (visible)
            {
                mainSplitter.Panel1Collapsed = false;
                mainSplitter.Panel2Collapsed = false;
                Inputter.SetFocus();
            }
            else
            {
                if (Core.Config.State.PostBoxInTop)
                    mainSplitter.Panel1Collapsed = true;
                else
                    mainSplitter.Panel2Collapsed = true;
                Inputter.Clear();
                if (Core.Config.Behavior.ClearReplyOnClosePostBox)
                    Inputter.ClearReplyTo();
            }
            Core.Config.State.ShowPostBox = visible;
            MPostBox.Checked = visible;
            TPostBox.Checked = visible;
            SPostBox.BorderStyle = visible ? Border3DStyle.SunkenOuter : Border3DStyle.Raised;
        }

        private void ToggleMenubar(object sender, EventArgs e)
        {
            Core.Config.State.ShowMenubar = !Core.Config.State.ShowMenubar;
            MMenubar.Checked = Core.Config.State.ShowMenubar;
            this.mainMenu.Visible = Core.Config.State.ShowMenubar;
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Menu && !Core.Config.State.ShowMenubar)
            {
                this.mainMenu.Visible = !this.mainMenu.Visible;
                mainMenu.Focus();
            }
            base.OnKeyDown(e);
        }

        void mainMenu_Leave(object sender, EventArgs e)
        {
            if (!Core.Config.State.ShowMenubar)
                this.mainMenu.Visible = false;
        }

        private void ToggleToolbar(object sender, EventArgs e)
        {
            Core.Config.State.ShowToolbar = !Core.Config.State.ShowToolbar;
            MToolbar.Checked = Core.Config.State.ShowToolbar;
            this.mainStrip.Visible = Core.Config.State.ShowToolbar;
        }

        private void ToggleStatusbar(object sender, EventArgs e)
        {
            Core.Config.State.ShowStatusbar = !Core.Config.State.ShowStatusbar;
            MStatusbar.Checked = Core.Config.State.ShowStatusbar;
            this.mainStatus.Visible = Core.Config.State.ShowStatusbar;
        }

        internal void ToggleTopMost(object sender, EventArgs e)
        {
            this.TopMost = !this.TopMost;
            MTopMost.Checked = this.TopMost;
            TTopMost.Checked = this.TopMost;
        }

        private void MCheckUpdate_Click(object sender, EventArgs e)
        {
            CheckUpdate();
        }

        internal void CheckUpdate()
        {
            SetBackStatusText(Lang.Main.CheckUpdate, true);
            Subsystem.CommonOperation.CheckUpdate();
            SetBackStatusText(Lang.Main.CheckUpdate_Complete);
        }

        private void ShowVersionInfo(object sender, EventArgs e)
        {
            using (var vform = new Forms.Dialogs.Show.Version())
            {
                vform.ShowDialog();
            }
        }

        #endregion

        private List<CustomContextMenuCandidate> ccMenuList = new List<CustomContextMenuCandidate>();
        internal CustomContextMenuCandidate[] CustomContextMenus
        {
            get { return ccMenuList.ToArray(); }
        }
        delegate void CCMenuInvoker(CustomContextMenuCandidate candidate);
        public void AddCustomContextMenuCandidate(CustomContextMenuCandidate candidate)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(
                    new CCMenuInvoker(AddCustomContextMenuCandidate),
                    candidate);
                return;
            }
            ccMenuList.Add(candidate);
        }

        Dictionary<string, ToolStripMenuItem> apMenuManager = new Dictionary<string, ToolStripMenuItem>();
        delegate void APMenuInvoker(IPlugin plugin, ToolStripMenuItem mitem);
        public void AddPluginMenu(IPlugin plugin, ToolStripMenuItem menuitem)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(
                    new APMenuInvoker(AddPluginMenu),
                    plugin, menuitem);
                return;
            }
            if (!apMenuManager.ContainsKey(plugin.Id))
            {
                apMenuManager.Add(plugin.Id, new ToolStripMenuItem(plugin.Name));
                MPlugin.DropDownItems.Add(apMenuManager[plugin.Id]);
            }
            apMenuManager[plugin.Id].DropDownItems.Add(menuitem);
        }

        delegate void SetStInvoker(string text);
        public void SetStatusText(string text)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(
                    new SetStInvoker(SetStatusText),
                    text);
                return;
            }
            if (text == null || text == String.Empty)
                StatusLabel.Text = Lang.Main.Status_Done;
            else
                StatusLabel.Text = text;
            mainStatus.Refresh();
        }

        public void SetStatusText(string text, int showLen)
        {
            Action<string, int> stmt = new Action<string, int>(_SetMT);
            stmt.BeginInvoke(text, showLen,
                (iar) => ((Action<string, int>)iar.AsyncState).EndInvoke(iar),
                stmt);
        }

        private void _SetMT(string text, int showlen)
        {
            SetStatusText(text);
            System.Threading.Thread.Sleep(showlen * 1000);
            SetStatusText(null);
        }

        public void SetBackStatusText(string text, bool keepalive)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(
                    new SetStInvoker(SetBackStatusText),
                    text);
                return;
            }
            if (text == null || text == String.Empty)
            {
                SBackJobLabel.Visible = false;
            }
            else
            {
                SBackJobLabel.Text = text;
                SBackJobLabel.Visible = true;
                if (!keepalive)
                {
                    var invoker = new MethodInvoker(_BackStatusTextInitializer);
                    invoker.BeginInvoke(_BackStatusTextInitEnd, invoker);
                }
            }
            mainStatus.Refresh();
        }
        #region BackStatusTextControl
        [EditorBrowsable(EditorBrowsableState.Never)]
        private void _BackStatusTextInitializer()
        {
            System.Threading.Thread.Sleep(Core.Config.Advanced.BackStateReinitWait);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        private void _BackStatusTextInitEnd(IAsyncResult iar)
        {
            ((MethodInvoker)iar.AsyncState).EndInvoke(iar);
            SetBackStatusText(String.Empty);
        }
        #endregion

        public void SetBackStatusText(string text)
        {
            SetBackStatusText(text, false);
        }

        delegate void RefreshTLInvoker(bool remeasure);
        public void RefreshTL(bool remeasure)
        {
            if (this.InvokeRequired)
                this.Invoke(new RefreshTLInvoker(RefreshTL), remeasure);
            else
                if (remeasure)
                    timeline.SurfaceRedraw(true);
                else
                    timeline.SurfaceRedraw(false);
        }

        public void RefreshTLWeak()
        {
            this.Invalidate();
            timeline.RefreshWeak();
        }

        private void MTimeline_DropDownOpening(object sender, EventArgs e)
        {
            MQuickTab.Enabled = xTab.SelectedItem != null;
        }

        private void TAddTab_DropDownOpening(object sender, EventArgs e)
        {
            TQuickTab.Enabled = xTab.SelectedItem != null;
        }

        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            switch (e.CloseReason)
            {
                case CloseReason.None:
                case CloseReason.UserClosing:
                    if (Core.Config.Behavior.ExitOnWindowClose)
                    {
                        Core.ExitApp();

                    }
                    else
                    {
                        e.Cancel = true;
                        this.Hide();
                    }
                    break;
            }
        }

        public void SetCursor(Cursor csor)
        {
            timeline.Cursor = csor;
        }

        internal enum SelectTargets { Above, Below, Top, End, Newest, AboveNew, BelowNew, Deselect };
        internal bool SetSelectStatus(SelectTargets target)
        {
            switch (target)
            {
                case SelectTargets.Above:
                    if (timeline.Statuses == null || timeline.SelectedIndex < 0) return false;
                    timeline.SelectedIndex--;
                    break;
                case SelectTargets.Below:
                    if (timeline.Statuses == null || timeline.SelectedIndex >= timeline.Statuses.Length - 1) return false;
                    timeline.SelectedIndex++;
                    break;
                case SelectTargets.AboveNew:
                    if (timeline.Statuses == null || timeline.SelectedIndex < 0) return false;
                    for (long i = timeline.SelectedIndex; i >= 0; i--)
                    {
                        if (timeline.Statuses[i].Unread)
                        {
                            timeline.SelectedIndex = i;
                            return true;
                        }
                    }
                    return false;
                case SelectTargets.BelowNew:
                    if (timeline.Statuses == null || timeline.SelectedIndex >= timeline.Statuses.Length - 1) return false;
                    for (long i = timeline.SelectedIndex; i < timeline.Statuses.Length - 1; i++)
                    {
                        if (i < 0) continue;
                        if (timeline.Statuses[i].Unread)
                        {
                            timeline.SelectedIndex = i;
                            return true;
                        }
                    }
                    return false;
                case SelectTargets.Deselect:
                    timeline.SelectedIndex = -1;
                    return true;
                case SelectTargets.Top:
                    if (timeline.Statuses == null) return false;
                    timeline.SelectedIndex = 0;
                    break;
                case SelectTargets.End:
                    if (timeline.Statuses == null) return false;
                    timeline.SelectedIndex = timeline.Statuses.Length - 1;
                    break;
                case SelectTargets.Newest:
                    if (timeline.Statuses == null) return false;
                    if (Core.Config.Timeline.ListBottomNewer)
                        timeline.SelectedIndex = timeline.Statuses.Length - 1;
                    else
                        timeline.SelectedIndex = 0;
                    break;
                default:
                    return false;
            }
            return true;
        }

        internal void SetScrollState(SelectTargets target)
        {
            switch (target)
            {
                case SelectTargets.Above:
                    int v = timeline.VerticalPosition;
                    v -= timeline.Height;
                    timeline.SetScrollTo(v);
                    break;
                case SelectTargets.Below:
                    int w = timeline.VerticalPosition;
                    w += timeline.Height;
                    timeline.SetScrollTo(w);
                    break;
                case SelectTargets.Top:
                    timeline.SetScrollTo(0);
                    break;
                case SelectTargets.End:
                    timeline.SetScrollTo(timeline.VerticalPositionMax);
                    break;
            }
        }

        public void EnsureVisible()
        {
            timeline.EnsureVisible();
        }

        internal bool SelectStatusByUID(ulong uid, bool evisible)
        {
            return timeline.SelectStatusByUID(uid, evisible);
        }

        internal void UpdateInfobar()
        {
            infoBar.UpdateDisplay();
        }

        private void infoBar_Click(object sender, EventArgs e)
        {
            using (var rep = new Dialogs.Show.Reporter())
            {
                rep.ShowDialog();
            }
        }

        private void Main_ClientSizeChanged(object sender, EventArgs e)
        {
            if (Core.Config.Behavior.SendTrayOnMinimize && this.WindowState == FormWindowState.Minimized)
            {
                this.Hide();
                this.WindowState = FormWindowState.Normal;
            }
        }

    }
}